<?php

class Rapide_Router_Rewrite implements Rapide_Router_Interface
{
	private
		$_oConfig,
		$_aRoutes = array(),
		$_iUrlLevel = 0;

	public function __construct()
	{
		$this->_oConfig = new Rapide_Config;

		$this->_oConfig->load('Router.php');
		$this->_oConfig->load('Controller.php');
	}

	/**
	 * Add route to route table
	 *
	 * @param String Pattern
	 * @param Array Route definition
	 */
	public function addRoute($sPattern, array $aRoute)
	{
		// Add route to routes table

		if(array_key_exists($sPattern, $this->_aRoutes))
		{
			$sError = sprintf('Pattern %s exists in routes table', $sPattern);
			throw new Rapide_Router_Rewrite_Exception($sError);
		}

		$this->_aRoutes[$sPattern] = $aRoute;
	}

	/**
	 *	Execute route process
	 *
	 *	@return Rapide_Dispatcher_Token token
	 */
	public function execute()
	{
		$sQuery = '/' . $this->_getQuery() . '/';

		$sController = null;
		$sAction = null;
		$aParameters = null;

		// Check routes table. If any request match, replace for new data.

		foreach($this->_aRoutes as $sPattern => $aRoute)
		{
			$aMatches = array();

			if(preg_match($sPattern, $sQuery, $aMatches))
			{
				if(!empty($aRoute['controller']))
				{
					$sController = $this->_routeReplace($aRoute['controller'], $aMatches);
				}

				if(!empty($aRoute['action']))
				{
					$sAction = $this->_routeReplace($aRoute['action'], $aMatches);
				}

				if(array_key_exists('parameters', $aRoute) && is_array($aRoute['parameters']))
				{
					foreach($aRoute['parameters'] as $sParameter => $sValue)
					{
						$aParameters[$sParameter] = $this->_routeReplace($sValue, $aMatches);
					}
				}

				break;
			}
		}

		$aQuery = explode('/', $this->_getQuery());

		if(empty($sController))
			$sController = $this->_getController($aQuery);

		if(empty($sAction))
			$sAction = $this->_getAction($aQuery);

		if(!is_array($aParameters))
		{
			if(!$this->_oConfig->get('Router', 'rewrite', 'segments'))
			{
				$aParameters = $this->_getParameters($aQuery);
			}
			else
				$aParameters = $aQuery;
		}

		// Save parameters to GET and REQUEST
		$_GET = $aParameters;
		$_REQUEST = array_merge($_REQUEST, $_GET);

		$oToken = new Rapide_Dispatcher_Token(ucfirst($sController), ucfirst($sAction), $aParameters);

		return $oToken;
	}

	private function _routeReplace($sString, $aMatches)
	{
		if(preg_match('/^:[0-9]+$/', $sString))
		{
			$iMatch	= (int)substr($sString, 1);
			$sString = $aMatches[$iMatch];
		}

		return $sString;
	}

	/**
	 * Get valid URL
	 *
	 * @param string $sController
	 * @param string $sAction
	 * @param array $aParameters for controller
	 * @param bool $bSSL
	 * @return string URL
	 */
	public function url($sController = null, $sAction = null, array $aParameters = array(), $bSSL = null)
	{
		$bSSL = isset($bSSL) ? (bool)$bSSL : $this->_oConfig->get('Router', 'ssl');

		$sURL = ($bSSL ? 'https' : 'http') . '://' . $this->_getUrl() . '/';
		
		if(!empty($sController))
		{
			$sURL .= $sController . '/';
		}

		if(!empty($sAction))
		{
				$sURL .= $sAction . '/';
		}

		if(count($aParameters))
		{
			if($this->_oConfig->get('Router', 'rewrite', 'segments'))
			{
				$sURL .= implode('/', $aParameters);
			}
			else
			{
				foreach($aParameters as $sParameter => $mValue)
				{
					if(!empty($mValue))
						$sURL .= $sParameter . '/' . $mValue . '/';
				}
			}
		}

		return $sURL;
	}

	private function _getQuery()
	{
		// Get query string without first and last slash

		$sQuery = substr($_SERVER['REQUEST_URI'], strlen(substr($_SERVER['SCRIPT_NAME'], 0, strlen($_SERVER['SCRIPT_NAME']) - 9)));

		if(substr($sQuery, 0, 1) == '/')
			$sQuery = substr($sQuery, 1, strlen($sQuery));

		if(substr($sQuery, -1, 1) == '/')
			$sQuery = substr($sQuery, 0, strlen($sQuery) - 1);

		return $sQuery;
	}

	private function _getController(array $aQuery)
	{
		// Get controller's name from query string

		return !empty($aQuery[0]) ?
					 $aQuery[0] :
					 $this->_oConfig->get('Controller', 'default', 'controller');
	}

	private function _getAction(array $aQuery)
	{
		// Get action's name from query string

		return !empty($aQuery[1]) ?
					 $aQuery[1] :
					 $this->_oConfig->get('Controller', 'default', 'action');
	}
	
	public function incoming()
	{
		// Return incoming url
		
		return $this->url() . $this->_getQuery();
	}

	private function _getParameters(array $aQuery)
	{
		// Get parameters array from query string

		$aParameters = array();
		$aValues = array();

		for($i = 2; $i < count($aQuery); $i++)
		{
			if($i % 2 == 0)
				$aParameters[] = $aQuery[$i];
			else
				$aValues[] = $aQuery[$i];
		}

		if(count($aParameters) != count($aValues))
			array_pop($aParameters);

		if(count($aParameters) && count($aValues))
			$aParameters = array_combine($aParameters, $aValues);

		return $aParameters;
	}

	/**
	 * Cut url of $this->_iUrlLevel levels.
	 * default: 0 - not cut.
	 */
	private function _getUrl() {

		$sUrl = $this->_oConfig->get('Router', 'url');

		if($this->_iUrlLevel) {
			$iUrlLevel = 0;
			for($i = strlen($sUrl) - 1; $i > 0; $i--) {
				if($sUrl{$i} == '/') $iUrlLevel++;
				
				if($this->_iUrlLevel == $iUrlLevel) {
					$sUrl = substr($sUrl, 0, $i);
					break;
				}
			}
		}

		return $sUrl;
	}

	public function setUrlLevel($iUrlLevel = 0) {
		$this->_iUrlLevel = (int)$iUrlLevel;
	}
}

?>